package per.joywong.blogauth.oauth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import per.joywong.blogauth.handler.BlogWebResponseExceptionTranslator;
import per.joywong.blogauth.response.token.BlogOauth2AccessToken;

import java.util.Set;

@Configuration
@EnableAuthorizationServer
public class AuthorizationSeverConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    BlogUserDetailsService userDetailsService;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    TokenStore redisTokenStore;

    @Autowired
    BlogWebResponseExceptionTranslator webResponseExceptionTranslator;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
        security.checkTokenAccess("isAuthenticated()");
        security.tokenKeyAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("blog-client")
                .secret(passwordEncoder.encode("blog-secret-8888"))
                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(7200)
                .scopes("all")
                .and()
                .withClient("user-client")
                .secret(passwordEncoder.encode("user-secret-8888"))
                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(7200)
                .scopes("all");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(redisTokenStore)
                .requestValidator(new OAuth2RequestValidator() {

                    @Override
                    public void validateScope(AuthorizationRequest authorizationRequest, ClientDetails client) throws InvalidScopeException {
                        System.out.println("33333333");
                        validateScope(authorizationRequest.getScope(), client.getScope());
                    }

                    @Override
                    public void validateScope(TokenRequest tokenRequest, ClientDetails client) throws InvalidScopeException {
                        System.out.println("44444444");
                        validateScope(tokenRequest.getScope(), client.getScope());
                    }

                    private void validateScope(Set<String> requestScopes, Set<String> clientScopes) {

                        if (clientScopes != null && !clientScopes.isEmpty()) {
                            for (String scope : requestScopes) {
                                if (!clientScopes.contains(scope)) {
                                    throw new InvalidScopeException("Invalid scope: " + scope, clientScopes);
                                }
                            }
                        }

                        if (requestScopes.isEmpty()) {
                            throw new InvalidScopeException("Empty scope (either the client or the user is not allowed the requested scopes)");
                        }
                    }
                })
                .tokenEnhancer(new TokenEnhancer() {
                    @Override
                    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                        System.out.println("000000000");
                        return new BlogOauth2AccessToken(accessToken);
                    }
                })
                .exceptionTranslator(webResponseExceptionTranslator);
    }
}
